/**
 *******************************************************************************
 * @file system_storage.c
 * @author .ISS_AUTO (hudandan@issauto.com)
 * @date 2024-03-23  1. Base version.
 *       2024-11-07  1. Update.
 *
 * @brief This file is interfaces for storage module.
 *
 * @copyright Copyright (c) 2024 ISSAUTO TECH Co., Ltd. All rights reserved.
 *
 *******************************************************************************
 */
/* Private Includes ----------------------------------------------------------*/
#include "system_storage.h"
#include "commun_protocol.h" //!< get_check_crc16();

/* Private define ------------------------------------------------------------*/
// #define ssm_log(M, ...)     USER_PRINTF("[SSM] "M"\r\n", ##__VA_ARGS__)
#define ssm_log(M, ...)

/* Private macro -------------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
 *                                   SSM
********************************************************************************/

/* Private variables ---------------------------------------------------------*/
static ssm_ee_param_typedef_t ssm_ee = {0};
#if defined(SP_PROJECT)
static ssm_user_info_typedef_t ssm_user = {0};
#endif

static ssm_info_typedef_t SSM_STORAGE_TABLE[SSM_FLASH_ID_MAX_NUM] = {
    {
        .name = "ee",
        .address = EEPROM_START_ADDRESS,
        .param = (void *) &ssm_ee,
    },
    #if defined(SP_PROJECT)
    {
        .name = "user",
        .address = ISS_FLASH_SSM_USER,
        .param = (void *) &ssm_user,
    },
    #endif
};

/* Private functions ---------------------------------------------------------*/


/* EEPROM USER CODE BEGIN ----------------------------------------------------*/

static __IO uint16_t EE_VIRT_ADDR_TAB[EE_VIRT_MAX_NUM]; //!< EE虚拟地址表

/**
 * @brief Reads a value from the EEPROM storage at the specified index.
 *
 * This function retrieves a 16-bit value from the EEPROM storage using the provided index.
 * It accesses the data through the SSM_STORAGE_TABLE structure.
 *
 * @param varIdx    - The index of the variable to read from the EEPROM storage.
 * @return uint16_t - The value read from the specified index in the EEPROM storage.
 */
uint16_t SSM_Info_Read_EE(uint16_t varIdx)
{
    ssm_ee_param_typedef_t *eeInfo = (ssm_ee_param_typedef_t *)SSM_STORAGE_TABLE[SSM_FLASH_ID_EE].param;
    return eeInfo->data[varIdx];
}

/**
 * @brief Writes a value to the EEPROM storage at the specified index.
 *
 * This function writes a 16-bit value to the EEPROM storage using the provided index.
 * It then reads back the written value to verify and update the local data structure.
 * Finally, it logs the operation details.
 *
 * @param varIdx - The index of the variable to write in the EEPROM storage.
 * @param new    - The new 16-bit value to be written.
 * @return none.
 */
void SSM_Info_Write_EE(uint16_t varIdx, uint16_t new)
{
    ssm_ee_param_typedef_t *eeInfo = (ssm_ee_param_typedef_t *)SSM_STORAGE_TABLE[SSM_FLASH_ID_EE].param;

    EE_WriteVariable(varIdx, new);
    (void)EE_ReadVariable(varIdx, &(eeInfo->data[varIdx]), 0);

    ssm_log("[%d]: %d (%2X), %d", varIdx, eeInfo->data[varIdx], eeInfo->data[varIdx], new);
}

/**
 * @brief Initializes the EEPROM storage (Write&Read operations).
 *
 * @param none.
 * @return none.
 */
static void SSM_Init_EE(void)
{
    for (int varIdx = 0; varIdx < EE_VIRT_MAX_NUM; varIdx++) {
        EE_VIRT_ADDR_TAB[varIdx] = EE_VIRT_ADD_VAR_MASK | varIdx;
    }

    E2PROM_Init((uint16_t *)EE_VIRT_ADDR_TAB, EE_VIRT_MAX_NUM);

}

/* EEPROM USER CODE END   ----------------------------------------------------*/


/**
 * @brief Return user info which is stored in flash.
 *
 * @param addr_id
 * @return user info in flash.
 */
void *SSM_Get_Info(uint8_t addr_id)
{
    void *pInfo = NULL;

    switch (addr_id) {
        case SSM_FLASH_ID_EE: {
            pInfo = (void *)SSM_STORAGE_TABLE[addr_id].param;
        }
        break;

        #if defined(SP_PROJECT)

        case SSM_FLASH_ID_USER: {
            pInfo = (void *)SSM_STORAGE_TABLE[addr_id].param;
        }
        break;
        #endif

        default:
            break;
    }

    return pInfo;
}

/**
 * @brief Read user info which is stored in flash.
 *
 * @param addr_id
 * @param user_info user info in flash.
 */
int SSM_Read_Info(uint8_t addr_id, void *pInfo)
{
    if (pInfo == NULL) return -2;

    switch (addr_id) {
        case SSM_FLASH_ID_EE: {
            ssm_ee_param_typedef_t *eeInfo = (ssm_ee_param_typedef_t *)SSM_STORAGE_TABLE[addr_id].param;
            memcpy((uint8_t *)pInfo, (uint8_t *)eeInfo, sizeof(ssm_ee_param_typedef_t));
        }
        break;

        #if defined(SP_PROJECT)

        case SSM_FLASH_ID_USER: {
            ssm_user_info_typedef_t *userInfo = (ssm_user_info_typedef_t *)SSM_STORAGE_TABLE[addr_id].param;
            memcpy((uint8_t *)pInfo, (uint8_t *)userInfo, sizeof(ssm_user_info_typedef_t));
        }
        break;
        #endif

        default:
            return -1;
    }

    return 0;
}

/**
 * @brief Write user info which is stored in flash.
 *
 * @param addr_id
 * @param pInfo user info in flash.
 */
int SSM_Write_Info(uint8_t addr_id, void *pInfo)
{
    if (pInfo == NULL) {
        return -2;
    }

    switch (addr_id) {
        case SSM_FLASH_ID_EE: {
            ssm_ee_param_typedef_t *eeInfo = (ssm_ee_param_typedef_t *)SSM_STORAGE_TABLE[addr_id].param;
            ssm_ee_param_typedef_t *input = (ssm_ee_param_typedef_t *)pInfo;

            input->info.flash_id = 0xEB90 | addr_id;

            for (int i = 0; i < EE_VIRT_MAX_NUM; i++) {
                EE_WriteVariable(i, input->data[i]);
            }

            for (int i = 0; i < EE_VIRT_MAX_NUM; i++) {
                (void)EE_ReadVariable(i, &(eeInfo->data[i]), 0);
            }

            ssm_log("[%s] >", SSM_STORAGE_TABLE[addr_id].name);

            for (size_t i = 0; i < EE_VIRT_MAX_NUM; i++) {
                ssm_log("[%d]: %d (%2X)", i, eeInfo->data[i], eeInfo->data[i]);
            }
        }
        break;

        #if defined(SP_PROJECT)

        case SSM_FLASH_ID_USER: {
            ssm_user_info_typedef_t *userInfo = (ssm_user_info_typedef_t *)SSM_STORAGE_TABLE[addr_id].param;
            ssm_user_info_typedef_t *input = (ssm_user_info_typedef_t *)pInfo;

            input->id = 0xEB900000 | addr_id;

            get_check_crc16(input->dname.str, input->dname.str_len, (uint8_t *) & (input->dname.check_sum[0]), 2); //!< 计算CRC16

            iss_flash_erase(SSM_STORAGE_TABLE[addr_id].address, sizeof(ssm_user_info_typedef_t));
            iss_flash_write(SSM_STORAGE_TABLE[addr_id].address, (void *)(input), sizeof(ssm_user_info_typedef_t));

            iss_flash_read(SSM_STORAGE_TABLE[addr_id].address, (void *)userInfo, sizeof(ssm_user_info_typedef_t));

            ssm_log("[%s] >", SSM_STORAGE_TABLE[addr_id].name);
            ssm_log("%s, len is %d",
                    userInfo->dname.str,
                    userInfo->dname.str_len);
        }
        break;
        #endif

        default:
            return -1;
    }

    return 0;
}

/**
 * @brief Default settings of device information
 *
 * @param addr_id
 */
void SSM_Write_Info_Default(uint8_t addr_id)
{
    switch (addr_id) {
        case SSM_FLASH_ID_EE: {
            ssm_ee_param_typedef_t tempInfo = {
                .data = {
                    0x0000,

                    #if defined(AK601)

                    #endif

                }
            };

            SSM_Write_Info(addr_id, (void *)&tempInfo);
        }
        break;

        #if defined(SP_PROJECT)

        case SSM_FLASH_ID_USER: {
            ssm_user_info_typedef_t tempInfo = {
                .id = 0,
                .dname = {
                    .str_len = strlen("simple_demo"),
                    .str = "simple_demo",
                    .check_sum = {0},
                },
            };

            SSM_Write_Info(addr_id, (void *)&tempInfo);
        }
        break;
        #endif

        default:
            break;
    }

}

/**
 * @brief Initialize user info which need stored in flash.
 *
 * @param none
 */
void SSM_Init(void)
{
    for (size_t addr_id = 0; addr_id < SSM_FLASH_ID_MAX_NUM; addr_id++) {
        uint8_t is_update = 1; //!< flash update

        /* 读取存储数据 - 设备信息 */
        switch (addr_id) {
            case SSM_FLASH_ID_EE: {
                SSM_Init_EE(); //!< 初始化EE读写模块

                ssm_ee_param_typedef_t *eeInfo = (ssm_ee_param_typedef_t *)SSM_STORAGE_TABLE[addr_id].param;

                //!< 读取历史存储值
                for (int i = 0; i < EE_VIRT_MAX_NUM; i++) {
                    (void)EE_ReadVariable(i, &(eeInfo->data[i]), 0);
                }

                if (eeInfo->info.flash_id == (0xEB90 | addr_id)) {
                    is_update = 0; //!< valid

                    ssm_log("[%s] >", SSM_STORAGE_TABLE[addr_id].name);

                    for (size_t i = 0; i < EE_VIRT_MAX_NUM; i++) {
                        ssm_log("[%d]: %d (%2X)", i, eeInfo->data[i], eeInfo->data[i]);
                    }
                }
            }
            break;

            #if defined(SP_PROJECT)

            case SSM_FLASH_ID_USER: {
                ssm_user_info_typedef_t *userInfo = (ssm_user_info_typedef_t *)SSM_STORAGE_TABLE[addr_id].param;

                //!< 读取历史存储值
                iss_flash_read(SSM_STORAGE_TABLE[addr_id].address, (void *)userInfo, sizeof(ssm_user_info_typedef_t));

                if (userInfo->id == (0xEB900000 | addr_id)) {
                    uint8_t check_crc[2] = {0};

                    if (userInfo->dname.str_len <= MAX_STR_LEN) {
                        get_check_crc16(userInfo->dname.str, userInfo->dname.str_len, (uint8_t *) & (check_crc[0]), 2); //!< 计算CRC16

                        ssm_log("\r\n[%s] > CheckSum is %02X %02X, CRC is %02X %02X.",
                                SSM_STORAGE_TABLE[addr_id].name,
                                userInfo->dname.check_sum[0], userInfo->dname.check_sum[1],
                                check_crc[0], check_crc[1]);

                        if ((check_crc[0] == userInfo->dname.check_sum[0]) && (check_crc[1] == userInfo->dname.check_sum[1])) {
                            is_update = 0; //!< valid

                            ssm_log("%s, len is %d.",
                                    userInfo->dname.str,
                                    userInfo->dname.str_len);
                        }
                    }
                }
            }
            break;
            #endif

            default:
                break;
        }

        if (is_update != 0) {
            SSM_Write_Info_Default(addr_id);
        }
    }
}

/* END OF FILE ---------------------------------------------------------------*/
